#include "STableColumnSet.h"

#include <core/FileSys.h>
#include <core/StyleMgr.h>

#include <QEvent>
#include <QHeaderView>
#include <QMenu>
#include <QMouseEvent>
#include <QScrollBar>
#include <QSettings>
#include <QTableView>
#include <QtGlobal>
#include <set>

#include "STable.h"
#include "STableSetDlg.h"

STableColumnSet::STableColumnSet(SVirtualTable* t, const QString& id,
                                 QObject* p)
    : QObject(p), m_table(t), m_idStr(id) {
  m_table->table()->installEventFilter(this);
}

void STableColumnSet::addColumnInfo(const ColumnInfo& ci) {
  Q_ASSERT(m_dftColumnInfos.find(ci.index) == m_dftColumnInfos.end());
  m_dftColumnInfos[ci.index] = ci;
}

void STableColumnSet::setColumnHide(int id, bool hide) {
  for (auto it = m_curColumnInfos.begin(); it != m_curColumnInfos.end(); ++it) {
    if (it->second.id == id) {
      if (hide) {
        it->second.curWidth = 0;
      } else {
        it->second.curWidth = it->second.dftWidth;
      }

      resizeColumn();
      break;
    }
  }
}

void STableColumnSet::setTableWidthFixed(bool fixed) {
  m_tableWidthFixed = fixed;
}

void STableColumnSet::apply(bool load) {
  m_doApply = true;
  if (m_saveCfg && load)
    loadCfg();
  else
    m_curColumnInfos = m_dftColumnInfos;

  QTableView* table = m_table->table();
  QStandardItemModel* model = qobject_cast<QStandardItemModel*>(table->model());
  if (model) {
    model->setColumnCount(m_curColumnInfos.size());
  }
  QStringList headerLabels;
  for (size_t col = 0; col < m_dftColumnInfos.size(); ++col) {
    table->horizontalHeader()->setSectionHidden(
        col, m_curColumnInfos[m_dftColumnInfos[col].curIndex].hide);
    headerLabels.push_back(m_dftColumnInfos[col].name);
    if (!model) {
      continue;
    }

    model->setHeaderData(col, Qt::Horizontal, (int)m_dftColumnInfos[col].align,
                         Qt::TextAlignmentRole);
    model->setHeaderData(col, Qt::Horizontal, m_dftColumnInfos[col].tooltip,
                         PrefixTipRole);
    model->setHeaderData(col, Qt::Horizontal, m_dftColumnInfos[col].name,
                         Qt::DisplayRole);
    model->setHeaderData(col, Qt::Horizontal,
                         StyleMgr::instance().color("TabBackNormal"),
                         Qt::BackgroundRole);
    model->setHeaderData(col, Qt::Horizontal,
                         StyleMgr::instance().color("ElementBackground"),
                         BorderColorRole);
    model->setHeaderData(col, Qt::Horizontal,
                         StyleMgr::instance().color("TitleWord"),
                         Qt::ForegroundRole);
  }

  for (auto col : m_curColumnInfos) {
    int from =
        m_table->table()->horizontalHeader()->visualIndex(col.second.index);
    int to = col.second.curIndex;
    if (from == to) continue;
    m_table->table()->horizontalHeader()->moveSection(from, to);
  }

  if (m_tableWidthFixed) {
    table->horizontalScrollBar()->hide();
    table->horizontalHeader()->setSectionResizeMode(QHeaderView::Fixed);
  } else {
    table->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive);
    table->horizontalHeader()->setStretchLastSection(true);
    for (size_t col = 0; col < m_dftColumnInfos.size(); ++col) {
      if (m_dftColumnInfos[col].widthType == kFixed)
        table->horizontalHeader()->setSectionResizeMode(col,
                                                        QHeaderView::Fixed);
    }
  }
  m_doApply = false;
}

void STableColumnSet::resizeColumn(bool useDft) {
  m_doResize = true;
  int viewWidth =
      m_table->frameGeometry().width() -
      (m_table->isVScrollBarShown() ? 8 : 0);  // 默认有垂直滚动条TODO没有
  if (auto* table_vscrollbar = m_table->table()->verticalScrollBar()) {
    viewWidth -= table_vscrollbar->isVisible() ? 8 : 0;
  }

  int totalWidth = 0;
  float totalRatioBase = 0;
  std::vector<int> cols;
  for (size_t col = 0; col < m_curColumnInfos.size(); ++col) {
    const ColumnInfo& ci = m_curColumnInfos[col];
    if (ci.hide) continue;

    if (ci.widthType == kFixed ||
        ci.widthType ==
            kManual)  // 固定宽度或者用户手动调整的，按照默认或用户调整的尺寸
    {
      m_table->table()->horizontalHeader()->resizeSection(col, ci.curWidth);
      viewWidth -= ci.curWidth;
    } else {
      float ratio = qMax(0.f, ci.ratio);
      totalRatioBase += ci.ratio;
      cols.push_back(col);
    }
  }

  if (qFuzzyCompare(totalRatioBase, 0.f)) {
    Q_ASSERT("配置错误！");
    totalRatioBase = 0.1f;
  }

  if (viewWidth < 0) viewWidth = 0;

  totalWidth = 0;
  if (!m_tableWidthFixed) {
    for (size_t i = 0; i < cols.size(); ++i) {
      // 查看默认宽度
      ColumnInfo& ci = m_curColumnInfos[cols[i]];
      int width = qMax(ci.dftWidth, ci.minWidth);
      width = qMax(width, 8);

      // 如果按比例的宽度更大，使用比例设置
      float ratio = qMax(0.f, ci.ratio);
      int ratioWidth = ci.ratio * viewWidth / totalRatioBase;

      width = qMax(width, ratioWidth);
      if (i == cols.size() - 1 && width + totalWidth < viewWidth)
        width = viewWidth - totalWidth;
      if (0 == ci.curWidth) width = 0;

      m_table->table()->horizontalHeader()->resizeSection(cols[i], width);
      ci.curWidth = width;
      totalWidth += width;
    }
  } else {
    for (size_t i = 0; i < cols.size(); ++i) {
      ColumnInfo& ci = m_curColumnInfos[cols[i]];
      int width = ci.ratio * viewWidth / totalRatioBase;
      width = qMax(width, 8);
      if (ci.minWidth > 0 && width < ci.minWidth) width = ci.minWidth;

      if (i == cols.size() - 1 && width + totalWidth < viewWidth)
        width = viewWidth - totalWidth;
      if (0 == ci.curWidth) width = 0;

      m_table->table()->horizontalHeader()->resizeSection(cols[i], width);
      ci.curWidth = width;
      totalWidth += width;
    }
  }

  saveCfg();
  m_doResize = false;
}

void STableColumnSet::applyChange(std::map<int, bool> changes) {
  bool changed = false;
  for (auto change : changes) {
    ColumnInfo& ci = m_curColumnInfos[change.first];
    if (change.second != ci.hide) continue;
    ci.hide = !change.second;
    m_table->table()->horizontalHeader()->setSectionHidden(ci.index, ci.hide);
    if (ci.name == "剩余期限" && ci.hide) emit clearHolidayMap();
    changed = true;
  }

  if (changed) resizeColumn();
}

bool STableColumnSet::isColVisible(int index) {
  if (m_dftColumnInfos.find(index) != m_dftColumnInfos.end()) {
    int curIndex = m_dftColumnInfos[index].curIndex;
    if (m_curColumnInfos.find(curIndex) != m_curColumnInfos.end())
      return !m_curColumnInfos[curIndex].hide;
  }

  return false;
}

bool STableColumnSet::isColVisible2(int id) {
  for (auto it = m_curColumnInfos.begin(); it != m_curColumnInfos.end(); ++it) {
    if (it->second.id == id) {
      return !it->second.hide;
    }
  }

  return false;
}

ColumnInfo STableColumnSet::colInfoByIndex(int index) {
  ColumnInfo ci;
  if (m_dftColumnInfos.find(index) != m_dftColumnInfos.end())
    ci = m_dftColumnInfos[index];

  return ci;
}

ColumnInfo STableColumnSet::colInfoById(int id) {
  ColumnInfo ci;
  for (auto it = m_dftColumnInfos.begin(); it != m_dftColumnInfos.end(); ++it) {
    if (it->second.id == id) {
      ci = it->second;
      break;
    }
  }

  return ci;
}

void STableColumnSet::setSaveCfg(bool v) {
  m_saveCfg = v;
  if (m_saveCfg) {
    connect(m_table->table()->horizontalHeader(), &QHeaderView::sectionMoved,
            this, &STableColumnSet::onHeaderSectionMoved);
    connect(m_table->table()->horizontalHeader(), &QHeaderView::sectionResized,
            this, &STableColumnSet::onHeaderSectionResized);
    m_table->table()->horizontalHeader()->viewport()->installEventFilter(this);
  }
}

void STableColumnSet::attachFrozenCols() {
  if (m_table->m_frozenTable)
    m_table->m_frozenTable->horizontalHeader()->viewport()->installEventFilter(
        this);
}

void STableColumnSet::setFrozenCols(int cols) {
  m_table->setFrozenCols(cols);
  attachFrozenCols();
}

std::map<int, std::vector<ColumnInfo> > STableColumnSet::groups() {
  std::map<int, std::vector<ColumnInfo> > group;
  for (const auto& col : m_curColumnInfos) {
    group[col.second.group].push_back(col.second);
  }

  for (auto& g : group) {
    std::sort(g.second.begin(), g.second.end(),
              [](const ColumnInfo& c1, const ColumnInfo& c2) {
                return c1.index < c2.index;
              });
  }
  return group;
}

bool STableColumnSet::eventFilter(QObject* watched, QEvent* event) {
  if (event->type() == QEvent::Resize && watched == m_table->table()) {
    resizeColumn();
  } else if (event->type() == QEvent::MouseButtonPress &&
             (watched == m_table->table()->horizontalHeader()->viewport() ||
              (m_table->m_frozenTable &&
               watched ==
                   m_table->m_frozenTable->horizontalHeader()->viewport()))) {
    QMouseEvent* me = dynamic_cast<QMouseEvent*>(event);
    if (me->button() == Qt::RightButton) {
      if (m_menuCfg == kNone) return QObject::eventFilter(watched, event);

      QMenu* menu = new QMenu();
      menu->setAttribute(Qt::WA_DeleteOnClose);

      if (m_menuCfg == KALl) {
        menu->addAction(tr("表格设置"), [=]() {
          STableSetDlg dlg(m_table, this);
          dlg.exec();

          emit tableSet();
        });
      }

      menu->addAction(tr("恢复默认"), [=]() { restoreDft(); });
      menu->popup(me->globalPos());
    }
  } else if (event->type() == QEvent::MouseMove &&
             watched == m_table->table()->horizontalHeader()) {
    QMouseEvent* me = dynamic_cast<QMouseEvent*>(event);
    if (me->button() == Qt::LeftButton) m_dragHeader = true;
  }
  return QObject::eventFilter(watched, event);
}

void STableColumnSet::saveCfg() {
  QSettings settings(
      FileSys::instance()->writeableAppCfgDir() + "/TableSettings.ini",
      QSettings::IniFormat);
  settings.beginGroup(m_idStr);
  for (const auto& col : m_curColumnInfos) {
    settings.setValue(col.second.name + "_index", col.second.curIndex);
    settings.setValue(col.second.name + "_hide", col.second.hide);
    settings.setValue(col.second.name + "_width", col.second.curWidth);
    settings.setValue(col.second.name + "_width_type", col.second.widthType);
  }
  settings.endGroup();
}

void STableColumnSet::loadCfg() {
  QSettings settings(
      FileSys::instance()->writeableAppCfgDir() + "/TableSettings.ini",
      QSettings::IniFormat);
  settings.beginGroup(m_idStr);

  std::vector<ColumnInfo> badCols;
  std::map<int, ColumnInfo> curColumnInfos;
  for (const auto& col : m_dftColumnInfos) {
    if (col.second.widthType == kFixed) {
      curColumnInfos[col.second.index] = col.second;
      continue;
    }

    int index =
        settings.value(col.second.name + "_index", col.second.index).toInt();
    bool hide =
        settings.value(col.second.name + "_hide", col.second.hide).toBool();
    float width =
        settings.value(col.second.name + "_width", col.second.dftWidth)
            .toFloat();
    ColWidthType wt =
        (ColWidthType)settings.value(col.second.name + "_width_type", kAuto)
            .toInt();

    ColumnInfo ci = col.second;
    ci.curIndex = index;
    ci.hide = hide;
    ci.widthType = wt;
    if (width < ci.minWidth) width = ci.minWidth;
    ci.curWidth = width;
    auto iter = curColumnInfos.find(index);
    if (iter != curColumnInfos.end())  // 索引错误！
    {
      badCols.push_back(ci);
    } else {
      curColumnInfos[ci.curIndex] = ci;
    }
  }
  if (!badCols.empty()) {
    int maxIndex = curColumnInfos.rbegin()->second.curIndex;
    for (auto& col : badCols) {
      col.curIndex = ++maxIndex;
      curColumnInfos[col.curIndex] = col;
    }
  }

  int index = 0;
  for (auto iter = curColumnInfos.begin(); iter != curColumnInfos.end();
       ++iter) {
    m_curColumnInfos[index] = iter->second;
    m_dftColumnInfos[iter->second.index].curIndex = index;
    ++index;
  }
  settings.endGroup();
}

void STableColumnSet::restoreDft() {
  for (auto& col : m_dftColumnInfos) col.second.curIndex = col.second.index;

  apply(false);
  resizeColumn(true);
}

void STableColumnSet::onHeaderSectionMoved(int logicalIndex, int oldVisualIndex,
                                           int newVisualIndex) {
  if (m_doApply) return;

  // 保险起见，全来一遍
  std::map<int, ColumnInfo> curColumnInfos;
  ;
  std::vector<ColumnInfo> badCols;
  std::set<int> colsIndex;
  for (size_t i = 0; i < m_dftColumnInfos.size(); ++i) {
    int oldIndex = m_dftColumnInfos[i].curIndex;
    int newIndex = m_table->table()->horizontalHeader()->visualIndex(i);
    bool exist = colsIndex.insert(newIndex).second == false;
    if (!exist) {
      curColumnInfos[newIndex] = m_curColumnInfos[oldIndex];
    } else
      badCols.push_back(m_curColumnInfos[oldIndex]);
  }

  m_curColumnInfos.clear();
  int index = 0;
  for (auto col : curColumnInfos) {
    col.second.curIndex = index;
    m_curColumnInfos[index] = col.second;
    ++index;
  }

  for (auto col : badCols) {
    col.index = index;
    m_curColumnInfos[index] = col;
    ++index;
  }

  for (auto col : m_curColumnInfos)
    m_dftColumnInfos[col.second.index].curIndex = col.second.curIndex;

  saveCfg();
}

void STableColumnSet::onHeaderSectionResized(int logicalIndex, int oldSize,
                                             int newSize) {
  if (!m_dragHeader || m_doApply || m_doResize ||
      m_curColumnInfos[m_dftColumnInfos[logicalIndex].curIndex].widthType ==
          kFixed)
    return;
  m_curColumnInfos[m_dftColumnInfos[logicalIndex].curIndex].curWidth = newSize;
  m_curColumnInfos[m_dftColumnInfos[logicalIndex].curIndex].widthType = kManual;
  saveCfg();
  m_dragHeader = false;
}
